#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#include "led_resources.h"
#include "led_operation.h"
#include "led_driver.h"

static int led_pin[100];
static int led_cnt = 0;

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       
{   
    //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    
    printk(KERN_INFO "init gpio: group %d, pin %d\n", GROUP(led_pin[which]), PIN(led_pin[which]));
    switch(GROUP(led_pin[which]))
    {
        case 0:
        {
            printk(KERN_INFO "init pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk(KERN_INFO "init pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk(KERN_INFO "init pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk(KERN_INFO "init pin of group 3 ...\n");
            break;
        }
    }
    
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    printk(KERN_INFO "set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(led_pin[which]), PIN(led_pin[which]));

    switch(GROUP(led_pin[which]))
    {
        case 0:
        {
            printk(KERN_INFO "set pin of group 0 ...\n");
            break;
        }
        case 1:
        {
            printk(KERN_INFO "set pin of group 1 ...\n");
            break;
        }
        case 2:
        {
            printk(KERN_INFO "set pin of group 2 ...\n");
            break;
        }
        case 3:
        {
            printk(KERN_INFO "set pin of group 3 ...\n");
            break;
        }
    }

    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init,
    .ctrl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

static int led_drv_probe(struct platform_device *dev){
    int pin_led;

    struct device_node *dev_node = dev->dev.of_node;

    if(dev_node == NULL)
        return -1;

    int err = of_property_read_u32(dev_node,"pin", &pin_led);
    if(err < 0) {
        printk(KERN_ERR "[%s]: of_property_read_u32 failed with error %d\n", __FUNCTION__, err);
        return err;
    }
    printk(KERN_INFO "[%s]pin get from dev_node is %d\n", __FUNCTION__, pin_led);

    led_pin[led_cnt] = pin_led;
    led_class_create_device(led_cnt);
    led_cnt++;

    return 0;
}

static int led_drv_remove(struct platform_device *dev){
    int i;
    int pin_led;

    struct device_node *dev_node = dev->dev.of_node;

    if(dev_node == NULL)
        return -1;

    int err = of_property_read_u32(dev_node,"pin", &pin_led);
    if(err < 0) {
        printk(KERN_ERR "[%s]: of_property_read_u32 failed with error %d\n", __FUNCTION__, err);
        return err;
    }

    for(i = 0; i < led_cnt; i++){
        if(led_pin[i] == pin_led){
            led_class_destroy_device(i);
            led_pin[i] =-1;
            break;
        }
    }
    /*if all the led device has been destoried , set led_cnt to 0*/    
    for(i = 0; i < led_cnt; i++){
        if(led_pin[i] != -1);
        break;
    }
    if(i == led_cnt)
        led_cnt = 0;

    return 0;
}

static struct of_device_id imx_led_id = {
    .compatible = "fison_led",
};

static struct platform_driver imx_led_drv ={
    .probe  = led_drv_probe,
    .remove = led_drv_remove,
    .driver ={
        .name           = "fison_led_platform",
        .of_match_table = &imx_led_id,
    } 
};

static int __init led_drv_init(void){
    int err = platform_driver_register(&imx_led_drv);
    register_led_operations(&board_demo_led_opr);
    return 0;
}

static void __exit led_drv_exit(void){
    platform_driver_unregister(&imx_led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_AUTHOR("fison");
MODULE_DESCRIPTION("led driver for imx6ull");
MODULE_LICENSE("GPL");